/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLLine2D.h>
#include <pgl/core/PGLLine2DPropPage.h>
#include <pgl/core/PGLDataVectors.h>
#include <pgl/core/DataRegistry.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

PGL_IMPLEMENT_DYNCREATE(CPGLLine2D, CPGLLine);

//! Save settings
void CPGLLine2D::SerializeXML(CPGLArchive& ar)
{
	CString strPos;	strPos.Format(_T("%u"),(DWORD)this);
	//modified by gale
	static const CString szTag = CString(this->GetRuntimeClass()->m_lpszClassName);//(TCHAR)this->GetRuntimeClass()->m_lpszClassName;
	//static const TCHAR szTag[] = _T("CPGLLine2D");

	CString datas, str;
	std::_tstring strX,strY;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		ASSERT(m_pX);
		ASSERT(m_pY);

		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();
		
			// base class
			CPGLLine::SerializeXML(ar);

			////////////////////////////////////////////////////////////////
			// Arrows
			xml.AddChildElem(_T("Arrows"));
			xml.IntoElem();
				xml.AddChildElem(_T("Start"));
				xml.IntoElem();
					xml.AddChildElemEx(_T("Visible"),m_StartArrow.m_bVisible);
					xml.AddChildElemEx(_T("Length"),m_StartArrow.m_dLength);
					xml.AddChildElemEx(_T("Thick"),m_StartArrow.m_dThick);
				xml.OutOfElem();

				xml.AddChildElem(_T("End"));
				xml.IntoElem();
					xml.AddChildElemEx(_T("Visible"),m_EndArrow.m_bVisible);
					xml.AddChildElemEx(_T("Length"),m_EndArrow.m_dLength);
					xml.AddChildElemEx(_T("Thick"),m_EndArrow.m_dThick);
				xml.OutOfElem();
			xml.OutOfElem();

			///////////////////////////////////////////////////////////////////
			// Data
			xml.AddChildElem(_T("Data"));
			xml.IntoElem();
				if (m_pX)
				{
					xml.AddChildElem(_T("X"));
					xml.IntoElem();
						m_pX->SerializeXML(ar);
					xml.OutOfElem();
				}

				if (m_pY)
				{
					xml.AddChildElem(_T("Y"));
					xml.IntoElem();
						m_pY->SerializeXML(ar);
					xml.OutOfElem();
				}
			xml.OutOfElem();
		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLLine::SerializeXML(ar);

			////////////////////////////////////////////////////////////////
			// Arrows
			if(xml.FindChildElem(_T("Arrows")))
			{
				xml.IntoElem();
					if (xml.FindChildElem(_T("Start")))
					{
						xml.IntoElem();
							if (xml.FindChildElem(_T("Visible")))
								m_StartArrow.m_bVisible=xml.GetChildDataBool();

							xml.ResetChildPos();
							if (xml.FindChildElem(_T("Length")))
								m_StartArrow.m_dLength=xml.GetChildDataDouble();

							xml.ResetChildPos();
							if (xml.FindChildElem(_T("Thick")))
								m_StartArrow.m_dThick=xml.GetChildDataDouble();
						xml.OutOfElem();
					}
					xml.ResetChildPos();

					if(xml.FindChildElem(_T("End")))
					{
						xml.IntoElem();
							if (xml.FindChildElem(_T("Visible")))
								m_EndArrow.m_bVisible=xml.GetChildDataBool();

							xml.ResetChildPos();
							if (xml.FindChildElem(_T("Length")))
								m_EndArrow.m_dLength=xml.GetChildDataDouble();

							xml.ResetChildPos();
							if (xml.FindChildElem(_T("Thick")))
								m_EndArrow.m_dThick=xml.GetChildDataDouble();
						xml.OutOfElem();
					}
				xml.OutOfElem();
			}
			
			//////////////////////////////////////////////////////////////////////////:
			/// Datas....
			if (xml.FindChildElem(_T("Data")))
			{
				xml.IntoElem();

				PGL_SERIALIZE_LOAD_DATA( strPos , X , m_pX );
				PGL_SERIALIZE_LOAD_DATA( strPos , Y , m_pY );

				xml.OutOfElem();
			}

		xml.OutOfElem();

		PostUpdateExtent();
	}
}

#ifdef _DEBUG
void CPGLLine2D::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLLine::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLLine2D ID ") << GetID() << "\n";
}
void CPGLLine2D::AssertValid() const
{
    // call inherited AssertValid first
    CPGLLine::AssertValid();
} 
#endif


CPGLLine2D::CPGLLine2D(const CPGLColor& _FrontColor, const CPGLColor& _FillColor)
: CPGLLine(_FrontColor, _FillColor)
{
	m_StartArrow.m_bVisible=m_EndArrow.m_bVisible=FALSE;
	m_StartArrow.m_dThick=m_EndArrow.m_dThick=12;
	m_StartArrow.m_dLength=m_EndArrow.m_dLength=16;
	m_bFilled=FALSE;

	LoadBitmap(IDB_PGL_LINE2D_BITMAP);
}

CPGLLine2D& CPGLLine2D::operator = (const CPGLLine2D& l)
{
	// prevent self copy
	if (&l != this)
	{
		// invoke CPGLLine copy assignement operator
		this->CPGLLine::operator=(l);

		m_StartArrow=l.m_StartArrow;
		m_EndArrow=l.m_EndArrow;

		m_bFilled = l.m_bFilled;

		ClearData();
		if (l.m_pX)
			m_pX=l.m_pX->Clone();
		if (l.m_pY)
			m_pY=l.m_pY->Clone();
	}
	return *this;
}

CPGLLine2D::~CPGLLine2D() 
{
	ClearData();
};

void CPGLLine2D::AddContextMenuItems(CMenu* pMenu)
{
	ASSERT_VALID(pMenu);
	// first call base class function
	CPGLLine::AddContextMenuItems(pMenu);

	// add separator
	// add own entries...
//	pMenu->AppendMenu(MF_ENABLED | MF_STRING, ,"Test Line2D");	
}

void CPGLLine2D::AddPropertyPage(CPropertySheet* pPropSheet)
{
	ASSERT_VALID(pPropSheet);
	// call own functions
	CPGLLine2DPropPage* propPage=new CPGLLine2DPropPage(this);
	pPropSheet->AddPage(propPage);

	// first call base class function
	CPGLLine::AddPropertyPage(pPropSheet);
}

void CPGLLine2D::SetDatas(const std::vector<double>& vx, const std::vector<double>& vy, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		SetDatas(CPGLVectorDPtr(new CPGLVectorD(vx)), CPGLVectorDPtr(new CPGLVectorD(vy)));
	}
	else
	{
		SetDatas(CPGLVectorRefDPtr(new CPGLVectorRefD(vx)), CPGLVectorRefDPtr(new CPGLVectorRefD(vy)));
	}
}

void CPGLLine2D::SetDatas(const std::vector<float>& vx, const std::vector<float>& vy, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		SetDatas(CPGLVectorFPtr(new CPGLVectorF(vx)), CPGLVectorFPtr(new CPGLVectorF(vy)));
	}
	else
	{
		SetDatas(CPGLVectorRefFPtr(new CPGLVectorRefF(vx)), CPGLVectorRefFPtr(new CPGLVectorRefF(vy)));
	}
}

void CPGLLine2D::SetDatas(const std::vector<int>& vx, const std::vector<int>& vy, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		SetDatas(CPGLVectorIPtr(new CPGLVectorI(vx)), CPGLVectorIPtr(new CPGLVectorI(vy)));
	}
	else
	{
		SetDatas(CPGLVectorRefIPtr(new CPGLVectorRefI(vx)), CPGLVectorRefIPtr(new CPGLVectorRefI(vy)));
	}
}

void CPGLLine2D::SetDatas(const std::vector<BYTE>& vx, const std::vector<BYTE>& vy, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		SetDatas(CPGLVectorBPtr(new CPGLVectorB(vx)), CPGLVectorBPtr(new CPGLVectorB(vy)));
	}
	else
	{
		SetDatas(CPGLVectorRefBPtr(new CPGLVectorRefB(vx)), CPGLVectorRefBPtr(new CPGLVectorRefB(vy)));
	}
}

void CPGLLine2D::SetDatas(const std::vector<float>::iterator startX, 
                          const std::vector<float>::iterator startY,
                          size_t numberOfPoints)
{
   SetDatas(CPGLVectorFPtr(new CPGLVectorF(&(*startX), numberOfPoints)), CPGLVectorFPtr(new CPGLVectorF(&(*startY), numberOfPoints)));
}

void CPGLLine2D::SetDatas(CPGLDataPtr pX, CPGLDataPtr pY)
{
	ClearData();

	m_pX = pX;
	m_pY = pY;

	PostUpdateExtent();
}


void CPGLLine2D::GetUnionExtent(gfxinterface::CGfxInterface& gfx, CPGLRectD& rExtent)
{
	CPGLObject::GetUnionExtent(gfx,rExtent);

	GetExtent().SetUnion(rExtent);

	rExtent.xValid = (m_pX.get() != NULL);
	rExtent.yValid = (m_pY.get() != NULL);
}

void CPGLLine2D::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	CPGLLine::UpdateExtent(gfx);

	m_rLimitIndices=CPGLRectU();

	UINT nPoints=GetNPoints();
	if (nPoints == 0)
	{
		SetExtent( CPGLRectD::GetNormBox() );
		return;
	}

	// updating...
	CPGLRectD rExtent;

	if (m_pX)
	{
		m_pX->GetMinMaxIndex(m_rLimitIndices.left, m_rLimitIndices.right);
		rExtent.left = (*m_pX)[m_rLimitIndices.left];
		rExtent.right = (*m_pX)[m_rLimitIndices.right];
	}

	if (m_pY)
	{
		m_pY->GetMinMaxIndex(m_rLimitIndices.bottom, m_rLimitIndices.top);
		rExtent.bottom = (*m_pY)[m_rLimitIndices.bottom];
		rExtent.top = (*m_pY)[m_rLimitIndices.top];
	}

	// setting extent
	SetExtent(rExtent);
}

void CPGLLine2D::PlotLineStripGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!m_pX || !m_pY || !IsContoured())
		return;

	ASSERT(m_pX);
	ASSERT(m_pY);
	
	DWORD lineType = GetELineType();

	switch (GetInterpolationType())
	{
	case InterpolationTypeStep:
		gfx.DrawStepStrip(*m_pX,*m_pY, lineType );
		break;
	case InterpolationTypeLinear:
		gfx.DrawLineStrip(*m_pX,*m_pY, lineType);
		break;
	case InterpolationTypeSpline:
		gfx.DrawLineStrip(*m_pX,*m_pY, lineType, m_dTension);
		break;
	case InterpolationTypeSegment:
		gfx.DrawMultipleLineStrip(m_iStripSize,*m_pX,*m_pY, lineType );
		break;
	}
}

void CPGLLine2D::PlotPointStripGfx(gfxinterface::CGfxInterface& gfx)
{
	if (m_pMarker)
		m_pMarker->PlotGfx(gfx, m_pX, m_pY);
}


void CPGLLine2D::PlotArrowsGfx(gfxinterface::CGfxInterface& gfx)
{
	/*
	double x,y,vx,vy,norm;

	// drawing arrows if needed
	if (GetNPoints()<=1)
		return;

	if (m_StartArrow.m_bVisible)
	{
		x=GetXi(1);
		y=GetYi(1);
		vx=GetXi(0)-GetXi(1);
		vy=GetYi(0)-GetYi(1);

		norm=sqrt(vx*vx+vy*vy);
		if (norm>PGL_EPS)
		{
//			vx*=0;
//			vy*=0;
			// starting path
			// sending points to eps
			GetColor().MakeCurrentFill(gfx);
			gfx.DrawArrowAbs(x, y, vx, vy, GetLineWidth()-1.0, m_StartArrow.m_dThick, m_StartArrow.m_dLength,true);
		}
	}

	if (m_EndArrow.m_bVisible)
	{
		x=GetXi(GetNPoints()-2);
		y=GetYi(GetNPoints()-2);
		vx=GetXi(GetNPoints()-1)-GetXi(GetNPoints()-2);
		vy=GetYi(GetNPoints()-1)-GetYi(GetNPoints()-2);

		norm=sqrt(vx*vx+vy*vy);
		if (norm>PGL_EPS)
		{
//			vx*=m_EndArrow.m_dLength/(norm);
//			vy*=m_EndArrow.m_dLength/(norm);
			// starting path
			// sending points to eps
			GetColor().MakeCurrentFill(gfx);
			gfx.DrawArrowAbs(x, y, vx, vy, GetLineWidth()-1.0, m_EndArrow.m_dThick, m_EndArrow.m_dLength,true);
		}
	}
	*/
}

void CPGLLine2D::ClearData()
{
	m_pX.reset();
	m_pY.reset();
}
